package org.example;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.example.annotation.Autowired;
import org.example.annotation.MyComponent;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// 之后都用注解  bean名默认为简单名
public class Ioc {

    private static final Map<String, Object> beanMap = new HashMap<>();

    //二级缓存 用于存放第一次的对象 防止循环依赖
    private static final Map<String, Object> secondCache = new HashMap<>();


    // 用于存放接口到bean的映射关系
    private static Map<String, Object> interfaceToBeanMap = new HashMap<>();



    //容器初始化  加载所有的bean  先到缓存中 然后构建接口 bean映射关系 最后注入 注入的时候 如果是接口 则可找其他 的
    static {
        //加载注解bean  默认
        try {
            handleAnnotation("org.example", secondCache);
            for(Map.Entry<String, Object> entry : secondCache.entrySet()){
                createBeanAndMap(entry.getKey(), entry.getValue());
            }

            performFieldInjection(secondCache);


        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }

    }



    // 创建 Bean 并建立映射关系
    public static void createBeanAndMap(String beanName, Object bean) {
        beanMap.put(beanName, bean);
        // 遍历 Bean 实现的接口，建立接口名与 Bean 的映射
        Class<?>[] interfaces = bean.getClass().getInterfaces();
        for (Class<?> inter : interfaces) {
            interfaceToBeanMap.put(inter.getSimpleName(), bean);
        }
    }

    //字段注入
    private static void performFieldInjection(Map<String, Object> cache) {
        for (Map.Entry<String, Object> entry : cache.entrySet()) {
            Object bean = entry.getValue();
            Class<?> clazz = bean.getClass();
            Field[] fields = clazz.getDeclaredFields();
            //对每个字段进行注入  如果有注解 则在map中找到 然后注入
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    field.setAccessible(true);
                    try {
                        String dependencyBeanName = field.getType().getSimpleName();
                        Object dependency = cache.get(dependencyBeanName);
                        if(dependency == null){
                            dependency = interfaceToBeanMap.get(dependencyBeanName);

                        }
                        if (dependency != null) {
                            field.set(bean, dependency);
                        }
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }


    // 直接返回泛型  可以让编译器自己推断返回哪种类型
    public static <T> T getBean(String beanName) {
        if (beanMap.containsKey(beanName)) {
            return (T) beanMap.get(beanName);
        }
        return (T) interfaceToBeanMap.get(beanName);
    }




    //扫描包 如果有注解 则创建bean 放到缓存中
    private static void handleAnnotation(String packageName, Map<String, Object> cache) throws IOException, ClassNotFoundException {
        // 获取当前线程的类加载器
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        // 将包名转换为文件路径
        String path = packageName.replace('.', '/');
        // 获取指定路径下的所有资源
        Enumeration<URL> resources = classLoader.getResources(path);
        while (resources.hasMoreElements()) {
            // 获取一个资源
            URL resource = resources.nextElement();
            // 将资源转换为文件对象
            File directory = new File(resource.getFile());
            if (directory.exists()) {
                // 递归处理目录及其子目录
                processDirectory(directory, packageName, cache);
            }
        }
    }


    //目录里的处理
    private static void processDirectory(File directory, String packageName, Map<String, Object> cache) throws ClassNotFoundException {
        // 获取目录下的所有文件
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    // 递归处理子目录
                    String subPackageName = packageName + '.' + file.getName();
                    processDirectory(file, subPackageName, cache);
                } else if (file.isFile() && file.getName().endsWith(".class")) {
                    // 构建类的全限定名
                    String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
                    // 加载类
                    Class<?> clazz = Class.forName(className);
                    // 检查类是否带有 MyComponent 注解
                    if (clazz.isAnnotationPresent(MyComponent.class)) {
                        // 获取 MyComponent 注解实例
                        MyComponent annotation = clazz.getAnnotation(MyComponent.class);
                        // 获取注解中指定的 Bean 名称
                        String beanName = annotation.value();
                        if (beanName.isEmpty()) {
                            // 如果注解中未指定 Bean 名称，则使用类的简单名称
                            beanName = clazz.getSimpleName();
                        }
                        try {
                            // 使用反射创建类的实例
                            Object bean = clazz.getDeclaredConstructor().newInstance();
                            // 将 Bean 实例存入 beanMap 中
                            cache.put(beanName, bean);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }


    @Deprecated
    static void handleXml() {
        //加载xml 到二进制流
        InputStream xmlStream = Ioc.class.getClassLoader().getResourceAsStream("bean.xml");
        //搞成dom
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(xmlStream);
            //找bean标签
            List<Element> list = document.getRootElement().selectNodes("bean");
            //遍历取出id和class
            for (Element o : list) {
                String id = o.attributeValue("id");
                String clazz = o.attributeValue("class");
                //用反射创建对象
                Object bean = Class.forName(clazz).newInstance();
                //放到map里
                beanMap.put(id, bean);

            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }


}
